package com.fyy.rtspdemo.factories;

import com.alibaba.fastjson2.util.IOUtils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.opencv.presets.opencv_core;

import javax.servlet.AsyncContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ConverterTranFactories extends Thread implements Converter {

    public volatile boolean runing = true;

    private FFmpegFrameGrabber grabber;

    private FFmpegFrameRecorder recorder;

    private byte[] headers;

    private ByteArrayOutputStream stream;

    private String url;

    private List<AsyncContext> outEntitys;

    private String key;

    private Map<String,Converter> factorries;

    public ConverterTranFactories(String url, String key, List<AsyncContext> outEntitys,
                                  Map<String,Converter> factorries,FFmpegFrameGrabber grabber){
        this.key = key;
        this.url = url;
        this.factorries = factorries;
        this.outEntitys = outEntitys;
        this.grabber = grabber;
    }

    @Override
    public void run() {
        try {
            System.out.println("this url converterTranFactories start " + url);
            grabber.setFrameNumber(25);
            if(grabber.getImageWidth() > 1920){
                grabber.setImageWidth(1920);
            }
            if(grabber.getImageHeight() > 1080){
                grabber.setImageHeight(1080);
            }
            stream = new ByteArrayOutputStream();
            recorder = new FFmpegFrameRecorder(stream,grabber.getImageWidth(),grabber.getImageHeight(),
                    grabber.getAudioChannels());
            recorder.setInterleaved(true);
            recorder.setVideoOption("preset", "ultrafast");
            recorder.setVideoOption("tune", "zerolatency");
            recorder.setVideoOption("crf", "25");
            recorder.setGopSize(50);
            recorder.setFrameRate(25);
            recorder.setSampleRate(grabber.getSampleRate());
            if(grabber.getAudioChannels() > 0){
                recorder.setAudioChannels(grabber.getAudioChannels());
                recorder.setAudioBitrate(grabber.getAudioBitrate());
                recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            }
            recorder.setFormat("flv");
            recorder.setVideoBitrate(grabber.getVideoBitrate());
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.start();
            if(headers == null){
                headers = stream.toByteArray();
                stream.reset();
                writeResponse(headers);
            }
            int nullNumber = 0;
            while (runing) {
                Frame f = grabber.grab();
                if(f != null){
                    try {
                        recorder.record(f);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if(stream.size() > 0){
                        byte[] b = stream.toByteArray();
                        stream.reset();
                        writeResponse(b);
                        if(outEntitys.isEmpty()){
                            System.out.println("没有输出，退出");
                            break;
                        }
                    }
                }else{
                    nullNumber++;
                    if(nullNumber > 200){
                        break;
                    }
                }
                Thread.sleep(5);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            closeConverter();
            completeResponse();
            factorries.remove(this.key);
        }
    }

    public void writeResponse(byte[] b) {
        Iterator<AsyncContext> it = outEntitys.iterator();
        while (it.hasNext()) {
            AsyncContext o = it.next();
            try {
                o.getResponse().getOutputStream().write(b);
            } catch (Exception e) {
                System.out.println("移除一个输出");
                it.remove();
            }
        }
    }

    public void closeConverter() {
        IOUtils.close(grabber);
        IOUtils.close(recorder);
        IOUtils.close(stream);
    }

    public void completeResponse() {
        Iterator<AsyncContext> it = outEntitys.iterator();
        while (it.hasNext()) {
            AsyncContext o = it.next();
            o.complete();
        }
    }

    @Override
    public String getKey() {
        return this.key;
    }

    @Override
    public String getUrl() {
        return this.url;
    }

    @Override
    public void addOutputStreamEntity(String key, AsyncContext entity) throws IOException {
        if(headers == null){
            outEntitys.add(entity);
        }else{
            entity.getResponse().getOutputStream().write(headers);
            entity.getResponse().getOutputStream().flush();
            outEntitys.add(entity);
        }
    }

    @Override
    public void exit() {
        this.runing = false;
        try {
            this.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
